#include "MarsPrecompile.h"
#include "MarsShaderCompiler.h"
#include "MarsFileSysMgr.h"
#include "MarsShader.h"

using namespace Graphics;

bool ShaderCompiler::CompileShader( Shader& shader )
{
	std::string content;
	if ( !FileSysMgr::Instance().LoadFileContent( shader.GetFileName(), content ) )
		return false;

	DWORD dwShaderCompileFlags = D3DCOMPILE_ENABLE_STRICTNESS;

#if defined( DEBUG ) || defined( _DEBUG )
	dwShaderCompileFlags |= D3DCOMPILE_DEBUG;
#endif

	ID3DBlob* errorBuffer = 0;
	HRESULT result = D3DX11CompileFromMemory(
		content.c_str(),
		content.size(),
		shader.GetFileName().c_str(),
		NULL,
		NULL,
		shader.GetEntry().c_str(),
		shader.GetShaderModel().c_str(),
		dwShaderCompileFlags,
		0,
		NULL,
		&shader.m_pBuffer,
		&errorBuffer,
		NULL
		);

	if ( FAILED( result ) )
	{
		if ( errorBuffer != 0 )
		{
			OutputDebugStringA( ( char* )errorBuffer->GetBufferPointer() );
			errorBuffer->Release();
		}

		return false;
	}

	if ( errorBuffer != 0 )
		errorBuffer->Release();

	ReflectionShader( shader );

	return true;
}

void ShaderCompiler::ReflectionShader( Shader& shader )
{
	ID3D11ShaderReflection* reflector = NULL;
	HRESULT hr = D3DReflect( shader.m_pBuffer->GetBufferPointer(), shader.m_pBuffer->GetBufferSize(), IID_ID3D11ShaderReflection, ( void** )&reflector );

	if ( FAILED( hr ) )
		return;

	// Read the constant table description.
	D3D11_SHADER_DESC shaderDesc;
	reflector->GetDesc( &shaderDesc );

	// Constant buffers
	for ( Core::uint32 i = 0; i < shaderDesc.BoundResources; ++i )
	{
		D3D11_SHADER_INPUT_BIND_DESC bindDesc;
		reflector->GetResourceBindingDesc( i, &bindDesc );
		Core::uint32 cbIndex = bindDesc.BindPoint;
		if ( bindDesc.Type == D3D_SIT_CBUFFER || bindDesc.Type == D3D_SIT_TBUFFER )
		{
			ID3D11ShaderReflectionConstantBuffer* constantBuffer = reflector->GetConstantBufferByName( bindDesc.Name );
			D3D11_SHADER_BUFFER_DESC cbDesc;
			constantBuffer->GetDesc( &cbDesc );

			// Track all of the variables in this constant buffer.
			for ( Core::uint32 constantIndex = 0; constantIndex < cbDesc.Variables; constantIndex++ )
			{
				ID3D11ShaderReflectionVariable* variable = constantBuffer->GetVariableByIndex( constantIndex );
				D3D11_SHADER_VARIABLE_DESC variableDesc;
				variable->GetDesc( &variableDesc );
				if ( variableDesc.uFlags & D3D10_SVF_USED )
				{
					ShaderParameter* pParameter = new ShaderParameter( variableDesc.Name, cbIndex, variableDesc.StartOffset, variableDesc.Size, 1 );
					shader.m_mapParameters.insert( std::make_pair( StrCRC::CRC( variableDesc.Name), pParameter ) );

					if ( shader.m_mapShaderSlots.find( cbIndex ) == shader.m_mapShaderSlots.end() )
					{
						shader.m_mapShaderSlots.insert( std::make_pair( cbIndex, Shader::ShaderSlot() ) );

						shader.m_mapShaderSlots[ cbIndex ].Index = cbIndex;
						shader.m_mapShaderSlots[ cbIndex ].Size = 0;
						shader.m_mapShaderSlots[ cbIndex ].Buffer = NULL;
						shader.m_mapShaderSlots[ cbIndex ].VBuffer = NULL;
						shader.m_mapShaderSlots[ cbIndex ].Dirty = true;
					}

					shader.m_mapShaderSlots[ cbIndex ].Size += variableDesc.Size;
				}
			}
		}
		else if ( bindDesc.Type == D3D_SIT_SAMPLER || bindDesc.Type == D3D_SIT_TEXTURE )
		{
			// ShaderParameter* pParameter = new ShaderParameter( bindDesc.Name, cbIndex, 0, 0, bindDesc.BindCount );
			// shader.m_mapParameters.insert( std::make_pair( bindDesc.Name, pParameter ) );

			// Find the texture that goes with this sampler
// 			for ( uint32 r2 = 0; r2 < shaderDesc.BoundResources; r2++ )
// 			{
// 				D3D11_SHADER_INPUT_BIND_DESC textureDesc;
// 				reflector->GetResourceBindingDesc( r2, &textureDesc );
// 				if ( textureDesc.Type == D3D_SIT_TEXTURE )
// 				{
// 					if ( !strcmp( textureDesc.Name, bindDesc.Name ) )
// 					{
// // 						Output.ParameterMap.AddParameterAllocation(
// // 							ANSI_TO_TCHAR(bindDesc.Name),
// // 							0,
// // 							TextureDesc.BindPoint,
// // 							TextureDesc.BindCount,
// // 							BindDesc.BindPoint
// // 							);
// 						break;
// 					}
// 				}
// 			}
		}
	}

	shader.GenerateUserDataBuffer();
	
	reflector->Release();
}
